package com.learn.xiaoha.sqlsession.utils;

import com.google.common.collect.Lists;
import com.learn.xiaoha.sqlsession.SqlSession;
import com.learn.xiaoha.sqlsession.factory.SimpleSqlSessionFactory;
import com.learn.xiaoha.sqlsession.result.handle.ListResultHandle;
import com.learn.xiaoha.sqlsession.result.handle.PojoResultHandle;
import com.learn.xiaoha.sqlsession.result.handle.ResultHandle;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import javax.sql.DataSource;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * JdbcExecute
 *
 * @author: cyx_jay
 * @date: 2021/2/2 15:35
 */
public class JdbcExecute {
    private static DataSource dataSource;


    public static ThreadLocal<SqlSession> sqlSessionThreadLocal = new ThreadLocal<>();

    public static SimpleSqlSessionFactory simpleSqlSessionFactory;



    public static void setDataSource(DataSource dataSource) {
        JdbcExecute.dataSource = dataSource;
    }

    public static DataSource getDataSource() {
        return dataSource;
    }

    /**
     * @Description sql连接
     * @Date 2021/2/2 14:35
     * @Param
     * @return
     */
    public static Connection getConnection(){
        SqlSession sqlSession = sqlSessionThreadLocal.get();
        if (sqlSession == null){
            sqlSessionThreadLocal.set(simpleSqlSessionFactory.getSqlSession());
        }
        return sqlSessionThreadLocal.get().getConnection();
    }

    /**
     * @Description 这里有规定 只能传入一个参数 若有多个请 封装参数
     * @Date 2021/2/2 15:25
     * @Param 执行查询
     * @return
     */
    public static Object executeSelect(String sql, Method method, Object[] args) throws Exception {
        Connection connection = getConnection();

        List<Object> params = getPreSql(sql, method.getReturnType(), args);
        //首先将sql标准化
        sql = sql.replaceAll(regex, "?");
        PreparedStatement preparedStatement = null;
        preparedStatement = connection.prepareStatement(sql);

        for (int i = 0; i < params.size(); i++) {
            preparedStatement.setObject(i+1,params.get(i));
        }
        ResultSet resultSet = preparedStatement.executeQuery();
        //依据返回的类型来处理相应的结果集
        return handleResult(method,resultSet);

    }

    private static Object handleResult(Method method, ResultSet resultSet) throws Exception {
        Class<?> returnType = method.getReturnType();
        boolean listFlag = returnType.isAssignableFrom(List.class);
        Object result = null;
        if (listFlag){
            //如果是集合
            ListResultHandle<Object> objectListResultHandle = new ListResultHandle<>();
            result = objectListResultHandle.handle(resultSet, getReallyType(method));
            return result;
        }else {
            PojoResultHandle<Object> objectPojoResultHandle = new PojoResultHandle<>();
            result = objectPojoResultHandle.handle(resultSet, returnType);
        }
        return result;
    }

    static Class getReallyType(Method method) throws ClassNotFoundException {
        if (method.getReturnType().isAssignableFrom(List.class)){
            Type type = method.getGenericReturnType();
            if (type instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
                //因为list泛型只有一个值 所以直接取0下标
                String typeName = actualTypeArguments[0].getTypeName();
                //真实返回值类型 Class对象
                Class<?> actualType = Class.forName(typeName);
                return actualType;
            }
        }
        return null;
    }


    private static List<Object> getPreSql(String sql,Class<?> returnType,Object[] args){
        List<Object> objects = new ArrayList<>();
        //获取参数的类型
        if (ArrayUtils.isEmpty(args)){
            return objects;
        }
        Object arg = args[0];
        boolean baseType = isBaseType(arg.getClass());
        //获取顺属性的值
        Map propertyNameToValueMap = getPropertyNameToValueMap(arg);
        if (!baseType){
            //不是基础类型

            Pattern p = Pattern.compile(regex);
            Matcher m = p.matcher(sql);
            while (m.find()){
                //取出来 {#id}
                int start = m.start();
                int end = m.end();
                //取除来id
                String property = sql.substring(start + 2, end-1);
                Object value = propertyNameToValueMap.get(property);
                objects.add(value);
            }

        }else {
            objects.add(arg);
        }
        return objects;

    }


    private static  final  String regex = "\\{#[^\\}]+\\}";


    static Map getPropertyNameToValueMap(Object object){
        Class<?> aClass = object.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        if (ArrayUtils.isNotEmpty(declaredFields)){
            HashMap<String, Object> hashMap = new HashMap<>();
            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                String name = declaredField.getName();
                try {
                    hashMap.put(name,declaredField.get(object));
                } catch (IllegalAccessException e) {

                }

            }
            return hashMap;
        }

        return new HashMap();

    }

    static boolean isBaseType(Class type){
        if (type == null){
            throw new RuntimeException("类型为空");
        }
        List<? extends Class<? extends Serializable>> baseClassList = Lists.newArrayList(String.class, Integer.class, Long.class);
        return baseClassList.contains(type);
    }


    public static Object executeUpdate(String sql, Method method,Object[] args) throws Exception {
        List<Object> params = getPreSql(sql, method.getReturnType(), args);
        sql = sql.replaceAll(regex, "?");
        PreparedStatement preparedStatement = null;
        Connection connection = getConnection();
        preparedStatement = connection.prepareStatement(sql);
        for (int i = 0; i < params.size(); i++) {
            preparedStatement.setObject(i+1,params.get(i));
        }
        return preparedStatement.execute();
    }
}
